﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Framework.ThreadWorker
{
    public abstract class BaseThreadWorker
    {
        protected Thread _thread;
        protected object _mutex = new object();
        protected Semaphore _semaphore = new Semaphore(0, 1);

        protected volatile bool _terminated = false;
        protected volatile bool _exit = false;
        protected volatile bool _suspend = false;
        protected WorkDelegate _workDelegate;
        protected int MaxJoinTimeout = 10000;

        public void Sleep(int timeout)
        {
            Thread.Sleep(timeout);
        }

        public delegate void WorkDelegate();

        protected virtual void Run()
        {
            while (!_exit)
            {
                try
                {
                    if (_suspend)
                    {
                        _semaphore.WaitOne();
                        _suspend = false;
                    }
                    if (_workDelegate != null)
                    {
                        _workDelegate.Invoke();
                    }
                    else
                    {
                        Work();
                    }
                    Thread.Sleep(GetWorkInterval());
                }
                catch (ThreadAbortException)
                {
                   break; 
                }
            }
            _terminated = true;
        }

        protected abstract void Work();
        protected abstract int GetWorkInterval();
        public virtual void Suspend()
        {
            _suspend = true;
        }

        public virtual  void Resume()
        {
            lock (_mutex)
            {
                try
                {
                    _semaphore.Release();
                }
                catch (Exception)
                {

                }
            }

        }

        public BaseThreadWorker()
        {

        }

        public BaseThreadWorker(WorkDelegate run)
        {
            _workDelegate = run;
        }

        public virtual void Start()
        {
            lock (_mutex)
            {
                if (_thread == null)
                {
                    _thread = new Thread(new ThreadStart(Run));
                }
                _thread.Start();
            }
        }

        public virtual void Stop(bool forcibly = false)
        {
            lock (_mutex)
            {
                _exit = true;
                if (_suspend)
                {
                    Resume();
                }
                if (forcibly)
                {
                    _thread.Abort();
                    _thread = null;
                }
                else
                {
                    _thread.Join(MaxJoinTimeout);
                    if (!_thread.ThreadState.Equals(ThreadState.Stopped))
                    {
                        _thread.Abort();
                        _thread = null;
                    }
                }
            }
        }

        public ThreadState GetState()
        {
            var state = ThreadState.Stopped;
            lock (_mutex)
            {
                if (_thread != null)
                {
                    state = _thread.ThreadState;
                }
                return state;
            }
        }
    }
}
